package edu.cuny.citytech.foreachlooptolambda.ui.refactorings; import java.text.MessageFormat; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.ITypeHierarchy; import org.eclipse.jdt.core.ITypeRoot; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.EnhancedForStatement; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext; import org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil; import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.NullChange; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import edu.cuny.citytech.foreachlooptolambda.ui.messages.Messages; import edu.cuny.citytech.foreachlooptolambda.ui.visitors.EnhancedForStatementVisitor; import edu.cuny.citytech.refactoring.common.core.Refactoring; /** * The activator class controls the plug-in life cycle * * @author <a href="mailto:rkhatchadourian@citytech.cuny.edu">Raffi * Khatchadourian & Md Arefin</a> */ public class ForeachLoopToLambdaRefactoring extends Refactoring { /** * The methods to refactor. */ private Set<IMethod> methods; /** * Creates a new refactoring with the given methods to refactor. * * @param methods * The methods to refactor. */ public ForeachLoopToLambdaRefactoring(IMethod... methods) { this.methods = new HashSet<IMethod>(Arrays.asList(methods)); } /** * Default constructor */ public ForeachLoopToLambdaRefactoring() { } @Override public String getName() { // TODO: Please rename. return Messages.ForEachLoopToLambdaRefactoring_Name; } @Override public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException { // TODO Empty for now. final RefactoringStatus status = new RefactoringStatus(); return status; } // this method get the EnhancedForSrarement to check the precondition private static Set<EnhancedForStatement> getEnhancedForStatements(IMethod method, IProgressMonitor pm) throws JavaModelException { ICompilationUnit iCompilationUnit = method.getCompilationUnit(); // get the ASTParser of the method CompilationUnit compilationUnit = RefactoringASTParser.parseWithASTProvider(iCompilationUnit, true, new SubProgressMonitor(pm, 1)); // get the method declaration ASTNode. MethodDeclaration methodDeclarationNode = ASTNodeSearchUtil.getMethodDeclarationNode(method, compilationUnit); final Set<EnhancedForStatement> statements = new LinkedHashSet<EnhancedForStatement>(); // extract all enhanced for loop statements in the method. methodDeclarationNode.accept(new ASTVisitor() { @Override public boolean visit(EnhancedForStatement node) { statements.add(node); return super.visit(node); } }); return statements; } @Override public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException { try { final RefactoringStatus status = new RefactoringStatus(); for (IMethod method : methods) { Set<EnhancedForStatement> statements = getEnhancedForStatements(method, new SubProgressMonitor(pm, 1)); IProgressMonitor subMonitor = new SubProgressMonitor(pm, statements.size()); // check preconditions on each. statements.stream().forEach(s -> status.merge(checkEnhancedForStatement(s, method, subMonitor))); pm.worked(1); } return status; } finally { pm.done(); } } // Checking if the EnhancedForLoop iterate over collection private static boolean enhancedForStatementIteratesOverCollection(EnhancedForStatement enhancedForStatement, IProgressMonitor pm) { Expression expression = enhancedForStatement.getExpression(); ITypeBinding nodeBindingType = expression.resolveTypeBinding(); // Checking if the enhancedForStatement implement or extend collection if (nodeBindingType.isArray()) { return true; } else { // getting the class-name to check if it's part of Collection String fullTypeName = nodeBindingType.getQualifiedName(); String typeName = fullTypeName.split("<")[0]; if (typeName.equals("java.util.Collection")) { return false; } // STEP 1: getting java the element of the type, IType iTypeElement = (IType) nodeBindingType.getJavaElement(); try { // STEP 2: getting java iTypeHeirchay, ITypeHierarchy iTypeHeirchay = iTypeElement.newSupertypeHierarchy(pm); // STEP 3: checking if Collections class being implemented IType[] superInterface = iTypeHeirchay.getAllInterfaces(); for (IType iType : superInterface) { String interfaceName = iType.getFullyQualifiedParameterizedName(); if (interfaceName.startsWith("java.util.Collection")) { return false; } } } catch (JavaModelException e) { e.printStackTrace(); } } return true; } // Checking with the precondiiton, private static RefactoringStatus checkEnhancedForStatement(EnhancedForStatement enhancedForStatement, IMethod method, IProgressMonitor pm) { try { RefactoringStatus status = new RefactoringStatus(); // create the visitor. EnhancedForStatementVisitor visitor = new EnhancedForStatementVisitor(enhancedForStatement, pm); // have the AST node "accept" the visitor. enhancedForStatement.accept(visitor); if (visitor.containsBreak()) { addWarning(status, Messages.ForEachLoopToLambdaRefactoring_ContainBreak, method); } if (visitor.containsContinue()) { addWarning(status, Messages.ForEachLoopToLambdaRefactoring_ContainContinue, method); } if (visitor.containsInvalidReturn()) { addWarning(status, Messages.ForEachLoopToLambdaRefactoring_ContainInvalidReturn, method); } if (visitor.containsMultipleReturn()) { addWarning(status, Messages.ForEachLoopToLambdaRefactoring_ContainMultipleReturn, method); } if (visitor.containsException()) { addWarning(status, Messages.ForEachLoopToLambdaRefactoring_ContainException, method); } if (enhancedForStatementIteratesOverCollection(enhancedForStatement, pm)) { addWarning(status, Messages.ForEachLoopToLambdaRefactoring_IteratesOverCollection, method); } // status.merge(checkMethodBody(method, new // SubProgressMonitor(pm,1))); pm.worked(1); return status; // passed. } finally { pm.done(); } } protected static RefactoringStatus checkMethodBody(IMethod method, IProgressMonitor pm) { RefactoringStatus status = new RefactoringStatus(); ITypeRoot root = method.getCompilationUnit(); CompilationUnit unit = RefactoringASTParser.parseWithASTProvider(root, false, new SubProgressMonitor(pm, 1)); MethodDeclaration declaration; try { declaration = ASTNodeSearchUtil.getMethodDeclarationNode(method, unit); if (declaration != null) { Block body = declaration.getBody(); if (body != null) { @SuppressWarnings("rawtypes") List statements = body.statements(); if (!statements.isEmpty()) { // TODO for now. addWarning(status, Messages.ForEachLoopToLambdaRefactoring_NoMethodsWithStatements, method); } } } } catch (JavaModelException e) { // TODO Auto-generated catch block e.printStackTrace(); } return status; } protected static void addWarning(RefactoringStatus status, String message, IMethod method) { status.addWarning(MessageFormat.format(message, method.getElementName()), JavaStatusContext.create(method)); } @Override public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { try { pm.beginTask(Messages.ForEachLoopToLambdaRefactoring_CreatingChange, 1); return new NullChange(getName()); } finally { pm.done(); } } }